package com.haiyang.flowablespringboot.service;

import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

@Service
@Slf4j
public class MyService {

    @Autowired
    ProcessEngine processEngine;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;


    @Transactional
    public void startProcess() {
        runtimeService.startProcessInstanceByKey("oneTaskProcess");
    }

    @Transactional
    public List<Task> getTasks(String assignee) {
        return taskService.createTaskQuery().taskAssignee(assignee).list();
    }

    //流程部署方式一 classPath部署  耦合度太高了，将资源文件嵌入项目文件
    public void processDeploy(String path) {
        //创建部署构建器
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        //指定加载资源的路径
        deploymentBuilder.addClasspathResource(path);
        //执行部署
        deploymentBuilder.deploy();
        //验证部署成功
        long count = repositoryService.createProcessDefinitionQuery().processDefinitionKey("singleTask").count();
        //如果count=1,部署成功
        if (count == 1) {
            System.out.println("部署成功");
        }
    }

    //流程部署方式二，springboot自动部署resources/processes目录下的配置文件

    //流程部署方式三 字符串部署，这个部署方式可以结合模版引擎技术渲染一些动态数据，比如人员，组织机构等
    public void processDeploy3() {
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"\n" +
                "             xmlns:flowable=\"http://flowable.org/bpmn\"\n" +
                "             typeLanguage=\"http://www.w3.org/2001/XMLSchema\" expressionLanguage=\"http://www.w3.org/1999/XPath\"\n" +
                "             targetNamespace=\"http://www.flowable.org/processdef\">\n" +
                "    <process id=\"singleTask2\" name=\"The One Task Process2\">\n" +
                "        <startEvent id=\"theStart\"/>\n" +
                "        <sequenceFlow id=\"flowl\" sourceRef=\"theStart\" targetRef=\"theTask\" />\n" +
                "        <userTask id=\"theTask\" name=\"my task\" flowable:assignee=\"zhangsan\"/>\n" +
                "        <sequenceFlow id=\"flow2\" sourceRef=\"theTask\" targetRef=\"theEnd\" />\n" +
                "        <endEvent id=\"theEnd\"/>\n" +
                "    </process>\n" +
                "</definitions>";
        //创建部署构建器
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        //指定部署文件名称和xml内容
        deploymentBuilder.addString("single-task2.bpmn20.xml", xml);
        //执行部署
        deploymentBuilder.deploy();
        //验证部署成功
        long count = repositoryService.createProcessDefinitionQuery().processDefinitionKey("singleTask2").count();
        //如果count=1,部署成功
        if (count == 1) {
            System.out.println("部署成功");
        }
    }

    // 流程部署方式四：流式部署，通过文件流进行部署
    public void processDeploy4() throws IOException {
        // 创建部署构建器
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();

        // 指定加载资源的路径
        Path filePath = Paths.get("D:\\Wang\\flowable-springboot\\src\\main\\resources\\single-task4.bpmn20.xml");
        try (InputStream inputStream = Files.newInputStream(filePath)) {
            // 将文件流添加到部署包中
            deploymentBuilder.addInputStream("single-task4.bpmn20.xml", inputStream);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }

        // 执行部署
        Deployment deploy = deploymentBuilder.deploy();

        // 验证部署是否成功
        long count = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("singleTask4")
                .count();

        // 如果 count = 1，表示部署成功
        if (count == 1) {
            System.out.println("部署成功");
        } else {
            System.out.println("部署失败，流程定义数量：" + count);
        }
    }

    //流程部署方式五：ZIP部署，通过文件流进行部署
    public String deployZip() throws Exception {
        File outfile = new File("D:\\Wang\\flowable-springboot\\src\\main\\resources\\single-task5.bpmn20.zip");
        FileInputStream inputStream = new FileInputStream(outfile);
        ZipInputStream ipInputStream = new ZipInputStream(inputStream);
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();        //创建部署对象
        deploymentBuilder.name("single-task5.bpmn20");                        //部署名称
        deploymentBuilder.addZipInputStream(ipInputStream);
        Deployment deployment = deploymentBuilder.deploy();    //完成部署
        ipInputStream.close();
        inputStream.close();
        return deployment.getId();                            //部署ID
    }

    //流程部署方式六：字节部署
    public void deployBytes() throws Exception {
        InputStream is = new FileInputStream("D:\\Wang\\flowable-springboot\\src\\main\\resources\\single-task6.bpmn20.xml");
        byte[] bytes = new byte[is.available()];
        is.read(bytes);
        is.close();
        Deployment deploy = repositoryService.createDeployment()
                .name("请假审批")
                .addBytes("LeaveProcess.bpmn20.xml", bytes)
                .deploy();
        // 验证部署是否成功
        long count = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("singleTask6")
                .count();

        // 如果 count = 1，表示部署成功
        if (count == 1) {
            System.out.println("部署成功");
        } else {
            System.out.println("部署失败，流程定义数量：" + count);
        }
    }

    //流程部署方式六：字节部署
    public void deployBpmnModel() {
        // StartEvent -flow1-> UserTask -flow2-> EndEvent
        SequenceFlow flow1 = new SequenceFlow();
        flow1.setId("flow1");
        flow1.setName("start-userTask1");
        flow1.setSourceRef("start");
        flow1.setTargetRef("userTask1");

        SequenceFlow flow2 = new SequenceFlow();
        flow1.setId("flow2");
        flow1.setName("userTask1-end");
        flow1.setSourceRef("userTask1");
        flow1.setTargetRef("end");

        StartEvent startEvent = new StartEvent();
        startEvent.setId("start");
        startEvent.setOutgoingFlows(Arrays.asList(flow1));

        UserTask userTask1 = new UserTask();
        userTask1.setId("userTask1");
        userTask1.setName("提交");
        userTask1.setIncomingFlows(Arrays.asList(flow1));
        userTask1.setOutgoingFlows(Arrays.asList(flow2));

        EndEvent endEvent = new EndEvent();
        endEvent.setId("end");
        endEvent.setIncomingFlows(Arrays.asList(flow2));


        BpmnModel bpmnModel = new BpmnModel();
        Process process = new Process();
        process.setId("HelloWorld");
        process.setName("请假审批");
        process.addFlowElement(startEvent);
        process.addFlowElement(flow1);
        process.addFlowElement(userTask1);
        process.addFlowElement(flow2);
        process.addFlowElement(endEvent);
        bpmnModel.addProcess(process);

        Deployment deploy = repositoryService.createDeployment()
                .name("自定义BpmnModel")
                .addBpmnModel("HelloWorld", bpmnModel)
                .deploy();

        // 验证部署是否成功
        long count = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("HelloWorld")
                .count();

        // 如果 count = 1，表示部署成功
        if (count == 1) {
            System.out.println("部署成功");
        } else {
            System.out.println("部署失败，流程定义数量：" + count);
        }
    }

    //获取流程部署列表数据
    public List<Deployment> getDeploymentList() {
        return repositoryService.createDeploymentQuery().list();
    }

    public List<ProcessDefinition> getProcessDefinitionList() {
        // 获取流程定义的图片
        String processDefinitionId = "myProcessDefinitionId"; // 替换为实际的流程定义 ID
        InputStream processDiagram = repositoryService.getProcessDiagram(processDefinitionId);
        return repositoryService.createProcessDefinitionQuery().list();
    }

    public void getProcessDiagram(String processDefinitionId) {
        // 获取流程定义的图片
        InputStream processDiagram = repositoryService.getProcessDiagram(processDefinitionId);
    }

    public void getProcessXMLText(String processDefinitionId) {
        // 获取流程定义对象
        ProcessDefinition processDefinition = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();

        if (processDefinition == null) {
            throw new IllegalArgumentException("未找到对应的流程定义: " + processDefinitionId);
        }

        // 获取流程定义XML文件的输入流，注意资源名称应为实际部署时的文件名
        InputStream inputStream = repositoryService.getResourceAsStream(
                processDefinition.getDeploymentId(),
                processDefinition.getResourceName() // 使用部署的资源名称
        );

        if (inputStream == null) {
            throw new FlowableObjectNotFoundException("未找到流程定义XML资源: " + processDefinition.getResourceName());
        }

        // 将输入流中的XML内容读取为字符串
        String xmlContent = new BufferedReader(new InputStreamReader(inputStream))
                .lines().collect(Collectors.joining("\n"));
        System.out.println("流程定义XML内容:\n" + xmlContent);
    }

    /**
     * deploymentId: 这是要删除的部署的唯一标识符（ID）。Flowable 通过这个 ID 来确定要删除哪个部署。
     * true: 这是一个布尔值，表示是否要级联删除相关的流程定义、流程实例、历史数据等。
     * 如果设置为 true，Flowable 会删除与该部署相关的所有数据；
     * 如果设置为 false，只删除部署本身，不会删除相关的流程定义和实例。
     */
    public void deleteDeployment(String deploymentId) {
        repositoryService.deleteDeployment(deploymentId,true);
    }
}
